<?php
/**
 * +----------------------------------------------------------------------
 * | CMPAY [ WE CAN DO IT JUST THINK ]
 * +----------------------------------------------------------------------
 * | Copyright (c) 2019 XingZKJ. All rights reserved
 * +----------------------------------------------------------------------
 * | Licensed ( https://opensource.org/licenses/MIT )
 * +----------------------------------------------------------------------
 * | Author: BrianWaring98 <BrianWaring98@gmail.com>
 * +----------------------------------------------------------------------
 */

namespace app\common\command;

use think\console\Command;
use think\console\Input;
use think\console\Output;
use think\Db;
use think\Log;
use Workerman\Worker;
use PHPSocketIO\SocketIO as SocketIOS;

class SocketIO extends Command
{
    /**
     * @var array 连线UID Array
     */
    protected $uidConnectionMap = [];

    /**
     * @var array
     */
    protected $sender_io = [];


    protected function configure()
    {
        $this->setName('socketio')->setDescription('用于支付页面推送成功提示');
    }

    /**
     * @param Input $input
     * @param Output $output
     *
     * @return int|void|null
     */
    protected function execute(Input $input, Output $output)
    {
        // PHPSocketIO服务
        $this->sender_io = new SocketIOS(2120);
        // 客户端发起连接事件时，设置连接socket的各种事件回调
        $this->sender_io->on('connection', function($socket){
            // 当客户端发来登录事件时触发
            $socket->on('login', function ($uid) use($socket){
                // 已经登录过了
                if(isset($socket->uid)){
                    return;
                }
                // 更新对应uid的在线数据
                $uid = (string)$uid;
                if(!isset($this->uidConnectionMap[$uid]))
                {
                    $this->uidConnectionMap[$uid] = 0;
                }
                // 这个uid有++$this->uidConnectionMap[$uid]个socket连接
                ++$this->uidConnectionMap[$uid];
                // 将这个连接加入到uid分组，方便针对uid推送数据
                $socket->join($uid);
                $socket->uid = $uid;

            });

            // 当客户端断开连接是触发（一般是关闭网页或者跳转刷新导致）
            $socket->on('disconnect', function () use($socket) {
                if(!isset($socket->uid))
                {
                    return;
                }
                // 将uid的在线socket数减一
                if(--$this->uidConnectionMap[$socket->uid] <= 0)
                {
                    unset($this->uidConnectionMap[$socket->uid]);
                }
            });
        });

        // 当$sender_io启动后监听一个http端口，通过这个端口可以给任意uid或者所有uid推送数据
        $this->sender_io->on('workerStart', function(){
            // 监听一个http端口
            $inner_http_worker = new Worker('http://0.0.0.0:2121');
            // 当http客户端发来数据时触发
            $inner_http_worker->onMessage = function($http_connection, $data){
                $data = $data['post'] ? $data['post'] : $data['get'];
                Log::notice(json_encode($data));
                // 推送数据的url格式 type=complete&to=trade_no&content=
                switch(isset($data['type'])){
                    case 'complete':
                        // 有指定uid则向uid所在socket组发送数据
                        if(isset($data['trade_no']) && isset($data['content'])){
                            $this->sender_io->to($data['trade_no'])->emit('success', $data['content']);
                            // 否则向所有uid推送数据
                        }
//                        else{
//                            $this->sender_io->emit('success', @$_POST['content'] . json_encode($this->uidConnectionMap));
//                        }
                        // http接口返回，如果用户离线socket返回fail
                        if($data['trade_no'] && !isset($this->uidConnectionMap[$data['trade_no']])){
                            return $http_connection->send('offline');
                        }else{
                            return $http_connection->send('success');
                        }
                }
                return $http_connection->send('fail');
            };
            // 执行监听
            $inner_http_worker->listen();
        });

        Worker::runAll();
    }
}